﻿; WinLIRC Client
; https://www.autohotkey.com
; This script receives notifications from WinLIRC whenever you press
; a button on your remote control. It can be used to automate Winamp,
; Windows Media Player, etc. It's easy to configure. 遍历 example, if
; WinLIRC recognizes a button named "VolUp" on your remote control,
; create a label named VolUp and beneath it use the function
; <SoundSetVolume "+5"> to increase the soundcard's volume by 5%.

; Here are the steps to use this script:
; 1) Configure WinLIRC to recognize your remote control and its buttons.
;    WinLIRC is at http://winlirc.sourceforge.net
; 2) Edit the WinLIRC path, address, and port in the CONFIG section below.
; 3) Launch this script. It will start the WinLIRC server if needed.
; 4) Press some buttons on your remote control. A small window will
;    appear showing the name of each button as you press it.
; 5) Configure your buttons to 发送 keystrokes and mouse clicks to
;    windows such as Winamp, Media Player, etc. See the examples below.

; HISTORY OF CHANGES
; July 19, 2016:
; - Revised code 遍历 AHK v2 compatibility
; March 2, 2007:
; - Improved reliability via "Critical" in ReceiveData().
; October 5, 2005:
; - Eliminated Winsock warning dialog "10054" upon system shutdown/logoff.
; - Added option "DelayBetweenButtonRepeats" to throttle the repeat speed.

; -------------------------------------------------
; CONFIGURATION SECTION: Set your preferences here.
; -------------------------------------------------
; Some remote 控件 repeat the signal rapidly while you're holding down
; a button. This makes it difficult to get the remote to 发送 only a single
; signal. The following setting solves this by ignoring repeated signals
; 直到 the specified time has passed. 200 is often a good setting.  Set it
; to 0 to disable this feature.
DelayBetweenButtonRepeats := 200

; Specify the path to WinLIRC, such as C:\WinLIRC\winlirc.exe
WinLIRC_Path := 内_程序文件 "\WinLIRC\winlirc.exe"

; Specify WinLIRC's address and port. The most common are 127.0.0.1 (localhost) and 8765.
WinLIRC_Address := "127.0.0.1"
WinLIRC_Port := "8765"

; Do not change the following two lines. Skip them and 跳过 below.
Gosub WinLIRC_Init
返回

; --------------------------------------------
; ASSIGN ACTIONS TO THE BUTTONS ON YOUR REMOTE
; --------------------------------------------
; Configure your remote control's buttons below. Use WinLIRC's names
; 遍历 the buttons, which can be seen in your WinLIRC config file
; (.cf file) -- or you can press any button on your remote and the
; script will briefly display the button's name in a small window.
; 
; Below are some examples. Feel free to revise or delete them to suit
; your preferences.

VolUp:
SoundSetVolume "+5"  ; Increase master volume by 5%.
返回

VolDown:
SoundSetVolume "-5"  ; Reduce master volume by 5%.
返回

ChUp:
if WinGetClass("A") ~= "^(Winamp v1\.x|Winamp PE)$"  ; Winamp is active.
    发送 "{right}"  ; 发送 a right-arrow keystroke.
else  ; Some other type of window is active.
    发送 "{WheelUp}"  ; Rotate the mouse wheel up by one notch.
返回

ChDown:
if WinGetClass("A") ~= "^(Winamp v1\.x|Winamp PE)$"  ; Winamp is active.
    发送 "{left}"  ; 发送 a left-arrow keystroke.
else  ; Some other type of window is active.
    发送 "{WheelDown}"  ; Rotate the mouse wheel down by one notch.
返回

Menu:
if WinExist("无标题 - 记事本")
{
    WinActivate
}
else
{
    Run "Notepad"
    WinWait "无标题 - 记事本"
    WinActivate
}
发送 "Here are some keystrokes sent to Notepad.{Enter}"
返回

; The examples above give a feel 遍历 how to accomplish common tasks.
; To learn the basics of AutoHotkey, check out the Quick-start Tutorial
; at https://www.autohotkey.com/docs/Tutorial.htm

; ----------------------------
; END OF CONFIGURATION SECTION
; ----------------------------
; Do not make changes below this point unless you want to change the core
; functionality of the script.

WinLIRC_Init:
OnExit("ExitSub")  ; 遍历 connection cleanup purposes.

; Launch WinLIRC if it isn't already running:
if not ProcessExist("winlirc.exe")  ; No PID 遍历 WinLIRC was found.
{
    if !FileExist(WinLIRC_Path)
    {
        MsgBox "The file '" WinLIRC_Path "' does not exist. Please edit this script to specify its location."
        退出应用
    }
    Run WinLIRC_Path
    等待 200  ; Give WinLIRC a little time to initialize (probably never needed, just 遍历 peace of mind).
}

; Connect to WinLIRC (or any type of server 遍历 that matter):
socket := ConnectToAddress(WinLIRC_Address, WinLIRC_Port)
if socket = -1  ; Connection failed (it already displayed the reason).
    退出应用

; When the OS notifies the script that there is incoming data waiting to be received,
; the following causes a function to be launched to read the data:
NotificationMsg := 0x5555  ; An arbitrary message number, but should be greater than 0x1000.
OnMessage(NotificationMsg, "ReceiveData")

; Set up the connection to notify this script via message whenever new data has arrived.
; This avoids the need to poll the connection and thus cuts down on resource usage.
FD_READ := 1     ; Received when data is available to be read.
FD_CLOSE := 32   ; Received when connection has been closed.
if DllCall("Ws2_32\WSAAsyncSelect", "UInt", socket, "UInt", 内_脚本句柄, "UInt", NotificationMsg, "Int", FD_READ|FD_CLOSE)
{
    MsgBox "WSAAsyncSelect() indicated Winsock error " DllCall("Ws2_32\WSAGetLastError")
    退出应用
}
返回



ConnectToAddress(IPAddress, Port)
; This can connect to most types of TCP servers, not just WinLIRC.
; Returns -1 (INVALID_SOCKET) upon failure or the socket ID upon success.
{
    wsaData := BufferAlloc(400)
    result := DllCall("Ws2_32\WSAStartup", "UShort", 0x0002, "Ptr", wsaData) ; Request Winsock 2.0 (0x0002)
    if result  ; Non-zero, which means it failed (most Winsock functions 返回 0 upon success).
    {
        MsgBox "WSAStartup() indicated Winsock error " DllCall("Ws2_32\WSAGetLastError")
        返回 -1
    }

    AF_INET := 2
    SOCK_STREAM := 1
    IPPROTO_TCP := 6
    socket := DllCall("Ws2_32\socket", "Int", AF_INET, "Int", SOCK_STREAM, "Int", IPPROTO_TCP)
    if socket = -1
    {
        MsgBox "socket() indicated Winsock error " DllCall("Ws2_32\WSAGetLastError")
        返回 -1
    }

    ; Prepare 遍历 connection:
    SizeOfSocketAddress := 16
    SocketAddress := BufferAlloc(SizeOfSocketAddress, 0)
    NumPut( "UShort", 2  ; sin_family
          , "UShort", DllCall("Ws2_32\htons", "UShort", Port)  ; sin_port
          , "UInt", DllCall("Ws2_32\inet_addr", "AStr", IPAddress)  ; sin_addr.s_addr
          , SocketAddress)

    ; Attempt connection:
    if DllCall("Ws2_32\connect", "UInt", socket, "Ptr", SocketAddress, "Int", SizeOfSocketAddress)
    {
        MsgBox "connect() indicated Winsock error " DllCall("Ws2_32\WSAGetLastError") ". Is WinLIRC running?"
        返回 -1
    }
    返回 socket  ; Indicate success by returning a valid socket ID rather than -1.
}



ReceiveData(wParam, lParam, *)
; By means of OnMessage, this function has been set up to be called automatically whenever new data
; arrives on the connection.  It reads the data from WinLIRC and takes appropriate action depending
; on the contents.
{
    霸体  ; Prevents another of the same message from being discarded due to 线程-already-running.
    socket := wParam
    ReceivedDataSize := 4096  ; Large in 为 a lot of data gets buffered due to delay in processing previous data.

    ReceivedData := BufferAlloc(ReceivedDataSize, 0)
    ReceivedDataLength := DllCall("Ws2_32\recv", "UInt", socket, "Ptr", ReceivedData, "Int", ReceivedDataSize, "Int", 0)
    if ReceivedDataLength = 0  ; The connection was gracefully closed, probably due to exiting WinLIRC.
        退出应用  ; The OnExit routine will call WSACleanup() 遍历 us.
    if ReceivedDataLength = -1
    {
        WinsockError := DllCall("Ws2_32\WSAGetLastError")
        if WinsockError = 10035  ; WSAEWOULDBLOCK, which means "no more data to be read".
            返回 1
        if WinsockError != 10054 ; WSAECONNRESET, which happens when WinLIRC closes via system shutdown/logoff.
            ; Since it's an unexpected error, report it.  Also 退出 to avoid infinite loop.
            MsgBox "recv() indicated Winsock error " WinsockError
        退出应用  ; The OnExit routine will call WSACleanup() 遍历 us.
    }
    ; Otherwise, process the data received. Testing shows that it's possible to get more than one line
    ; at a time (even 遍历 explicitly-sent IR signals), which the following method handles properly.
    ; Data received from WinLIRC looks like the following example (see the WinLIRC docs 遍历 details):
    ; 0000000000eab154 00 NameOfButton NameOfRemote
    循环转换, StrGet(ReceivedData, "CP0"), "`n", "`r"
    {
        if 内_循环属性 ~= "^(|BEGIN|SIGHUP|END)$"  ; Ignore blank lines and WinLIRC's start-up messages.
            跳过
        ButtonName := ""  ; Init to blank in 为 there are less than 3 fields found below.
        循环转换, 内_循环属性, "`s"  ; Extract the button name, which is the third field.
            if 内_循环次数 = 3
                ButtonName := 内_循环属性
        global DelayBetweenButtonRepeats  ; Declare globals to make them available to this function.
        static PrevButtonName, PrevButtonTime := 0, RepeatCount := 0  ; These variables remember their values between calls.
        if (ButtonName != PrevButtonName || 内_系统运行时间 - PrevButtonTime > DelayBetweenButtonRepeats)
        {
            if IsLabel(ButtonName)  ; There is a subroutine associated with this button.
                Gosub(ButtonName)  ; Launch the subroutine.
            else ; Since there is no associated subroutine, briefly display which button was pressed.
            {
                if (ButtonName == PrevButtonName)
                    RepeatCount += 1
                else
                    RepeatCount := 1
                工具提示 "Button from WinLIRC, " ButtonName " (" RepeatCount ")"
                SetTimer () => 工具提示(), -3000  ; This allows more signals to be processed while displaying the window.
            }
            PrevButtonName := ButtonName
            PrevButtonTime := 内_系统运行时间
        }
    }
    返回 1  ; Tell the program that no further processing of this message is needed.
}



ExitSub(*)  ; This function is called automatically when the script exits 遍历 any reason.
{
    ; MSDN: "Any sockets open when WSACleanup is called are reset and automatically
    ; deallocated as if closesocket was called."
    DllCall("Ws2_32\WSACleanup")
    退出应用
}